const db = require('./database').db
const schema = db.schema
const utils = require('./utils')

const session = opt => {

  const config = {
    sid: opt.sid || 'sid',
    path: opt.path || '/',
    secure: opt.secure || false,
    maxAge: opt.maxAge
  }

  init(config.maxAge)

  return config

}

const init = maxAge => {
  if(!maxAge || !db.link)
    return

  schema.session.collection.dropIndex('expires_1')
  schema.session.collection.createIndex({
    'expires': 1
  },{
    'expireAfterSeconds':maxAge / 1000,
    'background': true
  })
}

class request {
  constructor (config, req, res, next) {
    if(!db.link)
      return next()

    this.config = config
    this.req = req
    this.res = res
    this.next = next

    res.session = (sid, session) => {
      if(!session){
        return this.set(sid, session, this.session)
      }

      this.refresh(sid, null, false, true).then(docs => {
        this.set(sid, session, docs)
      })
    }

    res.delSession = (sid) => this.delSession(sid)

    req.getSession = sid => this.refresh(sid, null, false, true)

    if(req.headers.accept.indexOf('text/html') === -1){
      return next()
    }

    this.init(config, req, res, next)
  }

  init (config, req, res, next){
    this.getCookie()
  }

  set(sid, session, original) {
    if(!session){
      session = sid
      sid = this.sid
    }

    if(!sid || !session)
      return

    const find = {
      _id: sid
    }

    const update = {
      $set: {
        expires: Date.now(),
        data: JSON.stringify(utils.mergeJSON(session, original))
      }
    }

    const options = {}
    this.update(find, update, options)

  }

  refresh(sid, session, next, promise) {
    //sid, null, false, true
    const find = {
      _id: sid
    }

    const update = {
      $set: {
        expires: Date.now()
      }
    }

    const options = {
      upsert: true,
      new: true
    }

    if(session)
      update.$set.data = JSON.stringify(session)

    if(promise)
      return this.update(find, update, options, true, true)

    this.update(find, update, options, true)

  }

  getCookie() {
    const sid = this.req.cookies[this.config.sid]
    if(sid)
      this.setCookie(sid)
    else
      this.reCookie()
  }

  setCookie(sid) {
    const key = this.config.sid
    const value = sid || `${key}_${utils.randomString(25)}`
    const config = Object.assign({}, this.config)
    delete config.sid
    config.maxAge += Date.now()
    this.sid = value
    this.res.cookie(key, value, config)
    if(sid)
      this.refresh(value)
    else
      this.refresh(value, {})
  }

  reCookie() {
    const cookies = this.req.cookies
    const key = this.config.sid
    this.sid = cookies[key]
    this.setCookie(false)
  }

  update(find, update, options, next, promise) {

    if(promise)
      return new Promise((resolve, reject) => {
        this.changeDatabase(find, update, options, {resolve,reject})
      })

    this.changeDatabase(find, update, options, promise, next)
  }

  changeDatabase(find, update, options, promise, next) {
    schema.session.findOneAndUpdate(find, update, options, (err, docs) => {

      const resolve = promise ? promise.resolve : null
      const reject = promise ? promise.reject : null
      if(err)
        return reject ? reject(err) : console.error(err)
      if(resolve){
        const session = JSON.parse(docs.data || '{}')
        return resolve(session)
      }

      this.req.session = this.session = docs.data ? JSON.parse(docs.data) : {}

      if(next)
        this.next()
    })
  }

  delSession(sid) {
    schema.session.remove({_id: sid}, (err, docs) => {})
  }
}

module.exports = function(opt){

  const config = session(opt)
  return (req, res, next) => new request(config, req, res, next)

}
